package com.example.chat.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.example.chat.domain.GroupMember;
import com.example.chat.domain.GroupMessage;
import com.example.chat.mapper.GroupMemberMapper;
import com.example.chat.mapper.GroupMessageMapper;
import com.example.common.core.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket服务器端点
 * 
 * 处理WebSocket连接和消息传递的核心组件。
 * 支持通过JWT令牌进行身份验证和实时消息传递。
 */
@ServerEndpoint("/ws/chat")
@Component
public class WebSocketServer {

    // 将 groupMessageMapper 从实例变量改为静态变量
    private static GroupMessageMapper groupMessageMapper;
    
    @Autowired
    public void setGroupMessageMapper(GroupMessageMapper groupMessageMapper) {
        WebSocketServer.groupMessageMapper = groupMessageMapper;
    }

    static Log log= LogFactory.get(WebSocketServer.class);
    /**静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。*/
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话，需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId="";
    private String userName="";

    /**
     * JWT密钥，从配置文件中读取
     */
    @Value("${jwt.secret}")
    private String secret="zxcvbnmasdfghjuiyreqtuiwq";
    
    /**
     * 注入GroupMemberMapper - 由于WebSocket的特殊性，需要使用静态实例
     */
    private static GroupMemberMapper groupMemberMapper;
    
    @Autowired
    public void setGroupMemberMapper(GroupMemberMapper groupMemberMapper) {
        WebSocketServer.groupMemberMapper = groupMemberMapper;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        try {
            // 从URL中获取token参数
            String queryString = session.getQueryString();
            if (queryString != null && queryString.contains("token=")) {
                // 截取token值
                String token = queryString.substring(queryString.indexOf("token=") + 6);
                if (token.contains("&")) {
                    token = token.substring(0, token.indexOf("&"));
                }
                
                // 使用JwtUtils验证并解析JWT token
                try {
                    Claims claims = JwtUtils.parseToken(token, secret);
                    
                    // 从token中提取userId
                    this.userId = JwtUtils.getUserId(claims);
                    this.userName = JwtUtils.getUserName(claims);
                    
                    if (StrUtil.isBlank(this.userId)) {
                        log.error("Token中未包含有效的userId");
                        session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
                        return;
                    }
                } catch (Exception e) {
                    log.error("Token验证失败", e);
                    session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
                    return;
                }
            } else {
                log.error("缺少token参数");
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Missing token"));
                return;
            }
            // 存储连接
            if (webSocketMap.containsKey(userId)) {
                webSocketMap.remove(userId);
                webSocketMap.put(userId, this);
            } else {
                webSocketMap.put(userId, this);
                addOnlineCount();
            }
            log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("WebSocket连接异常", e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        if(StrUtil.isNotBlank(message)){
            try {
                //解析发送的报文
                JSONObject jsonObject = JSON.parseObject(message);
                String type = jsonObject.getString("type");
                if(type.equals("ping")){
                    return;
                }
                
                // 获取data对象
                JSONObject data = jsonObject.getJSONObject("data");
                if (data == null) {
                    log.warn("消息格式不正确，缺少data字段: " + message);
                    return;
                }
                
                String groupId = data.getString("groupId");
                if(StrUtil.isNotBlank(groupId)){
                    // 1. 保存消息到数据库
                    GroupMessage msg = new GroupMessage();
                    msg.setGroupId(Long.parseLong(groupId));
                    msg.setSenderId(Long.parseLong(this.userId));

                    msg.setSenderName(this.userName);
                    msg.setContent(data.getString("content"));
                    msg.setStatus(0); // 0-正常
                    groupMessageMapper.insert(msg);
                    
                    // 2. 构建消息对象
                    data.put("fromUserId", this.userId);
                    data.put("fromUserName", this.userName);
                    // 使用 ISO 8601 格式的时间戳
                    data.put("createTime", msg.getCreateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
                    // 3. 群发消息
                    List<GroupMember> members = groupMemberMapper.selectActiveMembers(Long.parseLong(groupId));
                    if (members != null && !members.isEmpty()) {
                        for (GroupMember member : members) {
                            String memberUserId = member.getUserId().toString();
                            if (webSocketMap.containsKey(memberUserId)) {
                                try {
                                    // 为每个接收者设置接收者信息
                                    data.put("toUserId", memberUserId);
                                    data.put("toUserName", member.getNickname());
                                    webSocketMap.get(memberUserId).sendMessage(jsonObject.toJSONString());
                                } catch (Exception e) {
                                    log.error("向群成员 " + memberUserId + " 发送消息失败: " + e.getMessage());
                                }
                            }
                        }
                    } else {
                        log.warn("群组 " + groupId + " 没有有效成员或不存在");
                    }
                }
            } catch (Exception e) {
                log.error("处理消息时发生错误: " + e.getMessage(), e);
            }
        }
    }
    
    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 发送自定义消息
     * */
    public static void sendInfo(String message, String userId) throws IOException {
        log.info("发送消息到:"+userId+"，报文:"+message);
        if(StrUtil.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            log.error("用户"+userId+",不在线！");
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}
